package com.swallow.auth.infrastructure.acl.oauth2.config.jwt;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.core.*;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.util.Assert;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.TimeZone;

/**
 * @author: yangjie.deng@resico.cn
 * @since: 2024-04-10 10:48:46
 * @version: v1.0.0
 * @describe: 自定义token校验器, 扩展基于token退出功能
 */
@Slf4j
public class CustomerJwtValidator implements OAuth2TokenValidator<Jwt> {
    private final Clock clock = Clock.system(TimeZone.getTimeZone("GTM+8").toZoneId());

    private static final String TOKEN_INVALID_FLAG = "metadata.token.invalidated";

    static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";

    private static final Duration DEFAULT_MAX_CLOCK_SKEW = Duration.of(60, ChronoUnit.SECONDS);

    private final OAuth2AuthorizationService authorizationService;

    public CustomerJwtValidator(OAuth2AuthorizationService authorizationService) {
        this.authorizationService = authorizationService;
    }

    @Override
    public OAuth2TokenValidatorResult validate(Jwt jwt) {
        Assert.notNull(jwt, "jwt cannot be null");
        Instant expiry = jwt.getExpiresAt();
        if (expiry != null) {
            if (Instant.now(this.clock).minus(DEFAULT_MAX_CLOCK_SKEW).isAfter(expiry)) {
                return OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, String.format("用户登录token已失效,失效时间: %s.", jwt.getExpiresAt()), ACCESS_TOKEN_REQUEST_ERROR_URI));
            }
        }
        Instant notBefore = jwt.getNotBefore();
        if (notBefore != null) {
            if (Instant.now(this.clock).plus(DEFAULT_MAX_CLOCK_SKEW).isBefore(notBefore)) {
                return OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, String.format("用户登录token已失效,失效时间: %s.", jwt.getExpiresAt()), ACCESS_TOKEN_REQUEST_ERROR_URI));
            }
        }
        // metadata.token.invalidated
        OAuth2Authorization auth2Authorization = authorizationService.findByToken(jwt.getTokenValue(), null);
        if (Objects.isNull(auth2Authorization)) {
            return OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "用户登录token已失效", ACCESS_TOKEN_REQUEST_ERROR_URI));
        }
        OAuth2Authorization.Token<OAuth2Token> token = auth2Authorization.getToken(jwt.getTokenValue());
        Object invalidFlag = token.getMetadata(TOKEN_INVALID_FLAG);

        if (Objects.nonNull(invalidFlag) && Boolean.TRUE.equals(Boolean.parseBoolean(invalidFlag.toString()))) {
            OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "用户登录token已失效", ACCESS_TOKEN_REQUEST_ERROR_URI);
            authorizationService.remove(auth2Authorization);
            return OAuth2TokenValidatorResult.failure(error);
        }

        return OAuth2TokenValidatorResult.success();
    }
}
